#ifndef __CORE_FEATURE_PLIC__
#define __CORE_FEATURE_PLIC__
/*!
 * @file     core_feature_plic.h
 * @brief    PLIC feature API header file for RISC-V Core
 */
/*
 * PLIC Feature Configuration Macro:
 * 1. __PLIC_PRESENT:  Define whether Platform-Level Interrupt Controller(PLIC) Unit is present or not
 *   * 0: Not present
 *   * 1: Present
 * 2. __PLIC_BASEADDR:  Base address of the PLIC unit.
 * 3. __PLIC_NIRQS   : Define the external interrupt number of PLIC Unit
 *
 */
#ifdef __cplusplus
extern "C" {
#endif

#include "core_feature_base.h"

#if defined(__PLIC_PRESENT) && (__PLIC_PRESENT == 1)
/**
 * \defgroup NMSIS_Core_PLIC_Registers     Register Define and Type Definitions Of PLIC
 * \ingroup NMSIS_Core_Registers
 * \brief   Type definitions and defines for plic registers.
 *
 * @{
 */


/**
 * \brief Access to the structure of PLIC Memory Map.
 */

#define PLIC_PRIORITY_OFS             (0x0000000UL)   /*!< PLIC Interrupt Source Priority Register */
#define PLIC_PENDING_OFS              (0x0001000UL)   /*!< PLIC Interrupt Pending Register */
#define PLIC_ENABLE_OFS               (0x0002000UL)   /*!< PLIC Interrupt Enable Register */
#define PLIC_THRESHOLD_OFS            (0x0200000UL)   /*!< PLIC Interrupt Priority Thrshold Register */
#define PLIC_COMPLETE_OFS             (0x0200004UL)   /*!< PLIC Interrupt Complete Register */
#define PLIC_CLAIM_OFS                (0x0200004UL)   /*!< PLIC Interrupt Claim Register */

#define PLIC_ENABLE_HARTS_OFS         (0x0000080UL)
#define PLIC_HANDLE_HARTS_OFS         (0x0001000UL)



#ifndef __PLIC_BASEADDR
/* Base address of PLIC(__PLIC_BASEADDR) should be defined in <Device.h> */
#error "__PLIC_BASEADDR is not defined, please check!"
#endif


/* PLIC Memory mapping of Device */
#define PLIC_BASE                          __PLIC_BASEADDR                            /*!< PLIC Base Address */
#define PLIC_INTNUM                        __PLIC_INTNUM                              /*!< PLIC Number Interrupt Source */

#define PLIC_ENABLE_BASE(hartid)              (uint32_t)((PLIC_BASE)+(PLIC_ENABLE_OFS   )+(PLIC_ENABLE_HARTS_OFS*hartid))
#define PLIC_THRESHOLD_BASE(hartid)           (uint32_t)((PLIC_BASE)+(PLIC_THRESHOLD_OFS)+(PLIC_HANDLE_HARTS_OFS*hartid))
#define PLIC_COMPLETE_BASE(hartid)            (uint32_t)((PLIC_BASE)+(PLIC_COMPLETE_OFS )+(PLIC_HANDLE_HARTS_OFS*hartid))
#define PLIC_CLAIM_BASE(hartid)               (uint32_t)((PLIC_BASE)+(PLIC_CLAIM_OFS    )+(PLIC_HANDLE_HARTS_OFS*hartid))

/** @} */ /* end of group NMSIS_Core_PLIC_Registers */

/* ##########################   PLIC functions  #################################### */
/**
 * \defgroup   NMSIS_Core_PLIC
 * \brief Functions that manage device interrupts via the PLIC.
 *
 * @{
 */
typedef void (*PLIC_Handler_Type)(void);
extern PLIC_Handler_Type PlicHandlers[PLIC_INTNUM];

#define PLIC_EnableIRQ               __PLIC_EnableIRQ
#define PLIC_GetEnableIRQ            __PLIC_GetEnableIRQ
#define PLIC_DisableIRQ              __PLIC_DisableIRQ
#define PLIC_GetPendingIRQ           __PLIC_GetPendingIRQ
#define PLIC_ClearPendingIRQ         __PLIC_ClearPendingIRQ
#define PLIC_SetPriorityIRQ          __PLIC_SetPriorityIRQ
#define PLIC_GetPriorityIRQ          __PLIC_GetPriorityIRQ
#define PLIC_SetThresholdIRQ         __PLIC_SetThresholdIRQ
#define PLIC_GetThresholdIRQ         __PLIC_GetThresholdIRQ
#define PLIC_SetHandler              __PLIC_SetHandler
#define PLIC_ExeHandler              __PLIC_ExeHandler
#define PLIC_Handler_Entry           __PLIC_Handler_Entry

__STATIC_FORCEINLINE void __PLIC_EnableHartIRQ(uint32_t hartid, PLIC_IRQn_Type IRQn)
{
    uint32_t *addr = (uint32_t *)(PLIC_ENABLE_BASE(hartid) + (IRQn >> 3));
    uint32_t ofs   = IRQn & 0x1F; //IRQn % 32
    *addr |= 1U << ofs;
}

__STATIC_FORCEINLINE uint32_t __PLIC_GetEnableHartIRQ(uint32_t hartid, PLIC_IRQn_Type IRQn)
{
    uint32_t *addr = (uint32_t *)(PLIC_ENABLE_BASE(hartid) + (IRQn >> 3));
    uint32_t ofs   = IRQn & 0x1F; //IRQn % 32
    return ((*addr >> ofs) & 0x1);
}

__STATIC_FORCEINLINE void __PLIC_DisableHartIRQ(uint32_t hartid, PLIC_IRQn_Type IRQn)
{
    uint32_t *addr = (uint32_t *)(PLIC_ENABLE_BASE(hartid) + (IRQn >> 3));
    uint32_t ofs   = IRQn & 0x1F; //IRQn % 32
    *addr &= ~(1U << ofs);
}

__STATIC_FORCEINLINE int32_t __PLIC_GetPendingHartIRQ(uint32_t hartid)
{

    uint32_t *addr = (uint32_t *)(PLIC_CLAIM_BASE(hartid));
    return *addr;
}

__STATIC_FORCEINLINE void __PLIC_ClearPendingHartIRQ(uint32_t hartid, PLIC_IRQn_Type IRQn)
{
    uint32_t *addr = (uint32_t *)(PLIC_COMPLETE_BASE(hartid));
    *addr = IRQn;
}

__STATIC_FORCEINLINE void __PLIC_SetThresholdHartIRQ(uint32_t hartid, uint8_t th)
{
    uint8_t *addr = (uint8_t *)(PLIC_THRESHOLD_BASE(hartid));
    *addr = th;
}

__STATIC_FORCEINLINE uint8_t __PLIC_GetThresholdHartIRQ(uint32_t hartid)
{
    uint8_t *addr = (uint8_t *)(PLIC_THRESHOLD_BASE(hartid));
    return *addr;
}


/**
 * \brief  Enable a specific interrupt
 * \details
 * This function enables the specific interrupt IRQn.
 * \param [in]  IRQn  Interrupt number
 * \remarks
 * - IRQn must not be negative.
 * \sa
 * - \ref PLIC_DisableIRQ
 */
__STATIC_FORCEINLINE void __PLIC_EnableIRQ(PLIC_IRQn_Type IRQn)
{
    uint32_t hartid = __get_hart_id();
    __PLIC_EnableHartIRQ(hartid, IRQn);
}

/**
 * \brief  Get a specific interrupt enable status
 * \details
 * This function returns the interrupt enable status for the specific interrupt \em IRQn.
 * \param [in]  IRQn  Interrupt number
 * \returns
 * - 0  Interrupt is not enabled
 * - 1  Interrupt is pending
 * \remarks
 * - IRQn must not be negative.
 * \sa
 * - \ref PLIC_EnableIRQ
 * - \ref PLIC_DisableIRQ
 */
__STATIC_FORCEINLINE uint32_t __PLIC_GetEnableIRQ(PLIC_IRQn_Type IRQn)
{
    uint32_t hartid = __get_hart_id();
    return __PLIC_GetEnableHartIRQ(hartid, IRQn);
}

/**
 * \brief  Disable a specific interrupt
 * \details
 * This function disables the specific interrupt \em IRQn.
 * \param [in]  IRQn  Number of the external interrupt to disable
 * \remarks
 * - IRQn must not be negative.
 * \sa
 * - \ref PLIC_EnableIRQ
 */
__STATIC_FORCEINLINE void __PLIC_DisableIRQ(PLIC_IRQn_Type IRQn)
{
    uint32_t hartid = __get_hart_id();
    __PLIC_DisableHartIRQ(hartid, IRQn);
}

/**
 * \brief  Get the current interrupt pending
 * \details
 * This function returns the current interrupt pending.
 * \returns
 * - 0  Interrupt is not pending
 * - 1  Interrupt is pending
 * \remarks
 * - IRQn must not be negative.
 * \sa
 * - \ref PLIC_ClearPendingIRQ
 */
__STATIC_FORCEINLINE uint32_t __PLIC_GetPendingIRQ()
{
    uint32_t hartid = __get_hart_id();
    return __PLIC_GetPendingHartIRQ(hartid);
}


/**
 * \brief  Clear a specific interrupt from pending
 * \details
 * This function removes the pending state of the specific interrupt \em IRQn.
 * \em IRQn cannot be a negative number.
 * \param [in]      IRQn  Interrupt number
 * \remarks
 * - IRQn must not be negative.
 * \sa
 * - \ref PLIC_GetPendingIRQ
 */
__STATIC_FORCEINLINE void __PLIC_ClearPendingIRQ(PLIC_IRQn_Type IRQn)
{
    uint32_t hartid = __get_hart_id();
    __PLIC_ClearPendingHartIRQ(hartid, IRQn);
}


/**
 * \brief  Set PLIC Interrupt priority of a specific interrupt
 * \details
 * This function get interrupt priority of the specific interrupt \em IRQn.
 * \param [in]      IRQn  Interrupt number
 * \param [in]      pri   Interrupt priority
 * \remarks
 * - IRQn must not be negative.
 * - If pri to be set is larger than the max priority allowed, it will be force to be max priority.
 * - Priority width is CLICINTCTLBITS minus clciinfo.nlbits if clciinfo.nlbits
 *   is less than CLICINTCTLBITS. Otherwise priority width is 0.
 * \sa
 * - \ref PLIC_GetPriorityIRQ
 */
__STATIC_FORCEINLINE void __PLIC_SetPriorityIRQ(PLIC_IRQn_Type IRQn, uint8_t pri)
{
    uint8_t *addr = (uint8_t *)(PLIC_PRIORITY_OFS + (IRQn << 2));
    *addr = pri;
}

/**
 * \brief  Get PLIC Interrupt priority of a specific interrupt
 * \details
 * This function get interrupt priority of the specific interrupt \em IRQn.
 * \param [in]      IRQn  Interrupt number
 * \return   Interrupt priority
 * \remarks
 * - IRQn must not be negative.
 * \sa
 * - \ref PLIC_SetPriorityIRQ
 */
__STATIC_FORCEINLINE uint8_t __PLIC_GetPriorityIRQ(PLIC_IRQn_Type IRQn)
{
    uint8_t *addr = (uint8_t *)(PLIC_PRIORITY_OFS + (IRQn << 2));
    return *addr;
}

/**
 * \brief  Set PLIC Interrupt priority threshold
 * \details
 * This function get interrupt priority threshold.
 * \param [in]      th   Interrupt priority threshold
 * \remarks
 * - IRQn must not be negative.
 * - If pri to be set is larger than the max priority allowed, it will be force to be max priority.
 * - Priority width is CLICINTCTLBITS minus clciinfo.nlbits if clciinfo.nlbits
 *   is less than CLICINTCTLBITS. Otherwise priority width is 0.
 * \sa
 * - \ref PLIC_GetThresholdIRQ
 */
__STATIC_FORCEINLINE void __PLIC_SetThresholdIRQ(uint8_t th)
{
    uint32_t hartid = __get_hart_id();
    __PLIC_SetThresholdHartIRQ(hartid, th);
}

/**
 * \brief  Get PLIC Interrupt priority threshold
 * \details
 * This function get interrupt priority threshold.
 * \return   Interrupt priority threshold
 * \remarks
 * - IRQn must not be negative.
 * - If pri to be set is larger than the max priority allowed, it will be force to be max priority.
 * - Priority width is CLICINTCTLBITS minus clciinfo.nlbits if clciinfo.nlbits
 *   is less than CLICINTCTLBITS. Otherwise priority width is 0.
 * \sa
 * - \ref PLIC_GetThresholdIRQ
 */
__STATIC_FORCEINLINE uint8_t __PLIC_GetThresholdIRQ(void)
{
    uint32_t hartid = __get_hart_id();
    return __PLIC_GetThresholdHartIRQ(hartid);
}

/**
 * \brief  Set PLIC Interrupt handler function of a specific interrupt
 * \details
 * This function get interrupt priority of the specific interrupt \em IRQn.
 * \param [in]      IRQn  Interrupt number
 * \param [in]      Handler Interrupt handler function
 * \remarks
 * - IRQn must not be negative.
 * - If pri to be set is larger than the max priority allowed, it will be force to be max priority.
 * - Priority width is CLICINTCTLBITS minus clciinfo.nlbits if clciinfo.nlbits
 *   is less than CLICINTCTLBITS. Otherwise priority width is 0.
 * \sa
 * - \ref PLIC_GetPriorityIRQ
 */
__STATIC_FORCEINLINE void __PLIC_SetHandler(PLIC_IRQn_Type IRQn, PLIC_Handler_Type handler)
{
    PlicHandlers[IRQn] = handler;
}


__STATIC_FORCEINLINE void __PLIC_ExeHandler(void)
{
    uint32_t IRQn = __PLIC_GetPendingIRQ();
    if((IRQn >= 1) && (IRQn < PLIC_INT_MAX))
        PlicHandlers[IRQn]();
    else
        return;
    __PLIC_ClearPendingIRQ(IRQn);
}

__STATIC_FORCEINLINE void __PLIC_Handler_Entry(void)
{
    __IBEX_Save_Context();
    __PLIC_ExeHandler();
    __IBEX_Restore_Context();
}



#endif /* defined(__PLIC_PRESENT) && (__PLIC_PRESENT == 1) */

#ifdef __cplusplus
}
#endif
#endif /* __CORE_FEATURE_PLIC__ */
